package com.wennn.top.security.service;

import com.google.common.collect.Lists;
import com.wennn.top.core.cache.RedisUtils;
import com.wennn.top.security.config.JwtSetting;
import com.wennn.top.security.model.SessionContext;
import com.wennn.top.security.model.SessionUser;
import com.wennn.top.security.model.token.JwtAccessToken;
import com.wennn.top.security.model.token.JwtRawAccessToken;
import com.wennn.top.security.model.token.JwtToken;
import com.wennn.top.security.util.SecurityConst;
import com.wennn.top.util.JsonUtil;
import io.jsonwebtoken.*;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.www.NonceExpiredException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @description: token 生成工厂
 * @author: wennn
 * @date: 5/9/20
 */
@Service
public class JwtTokenFactory {

    @Autowired @Getter
    JwtSetting jwtSetting;


    @Autowired
    RedisUtils redisUtils;

    /**
     * @description: 依据session加密信息
     * @param: SessionContext 用户session
     * @return: JwtAccessToken 加密token
     * @author: wennn
     * @date: 5/9/20 4:40 PM
     */
    public JwtAccessToken create(SessionContext sessionContext) {

        if (StringUtils.isEmpty(sessionContext.getUsername())) {
            throw new IllegalArgumentException("用户名称不能为空");
        }

        if (sessionContext.getAuthorities() == null || sessionContext.getAuthorities().isEmpty()) {
            throw new IllegalArgumentException("用户权限信息不能为空");
        }

        Claims claims = Jwts.claims().setSubject(sessionContext.getUsername());

        claims.put(SecurityConst.JWT_CLAIMS_SCOPE_KEY, sessionContext.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(",")));
//        claims.put(SecurityConst.JWT_CLAIMS_USER_KEY, JsonUtil.toJson(sessionContext.getSe    ssionUser()));

//        claims.put(SecurityConst.JWT_CLAIMS_SCOPE_KEY, sessionContext.getAuthorities());
        claims.put(SecurityConst.JWT_CLAIMS_USER_KEY, sessionContext.getSessionUser());

        // 加密
        String token = builderJwt(claims).compact();

        return new JwtAccessToken(token, claims);
    }


    /**
     * 刷新token
     *
     * @param sessionContext
     * @return
     */
    public JwtToken createRefresh(SessionContext sessionContext) {
        if (StringUtils.isEmpty(sessionContext.getUsername())) {
            throw new IllegalArgumentException("用户名称不能为空");
        }

        Claims claims = Jwts.claims().setSubject(sessionContext.getUsername());

        // 加密
        String token = builderJwt(claims).
                setId(UUID.randomUUID().toString())
                .compact();

        return new JwtAccessToken(token, claims);
    }


    /*
     * 构建JwtBuilder
     */
    private JwtBuilder builderJwt(Claims claims) throws RuntimeException {
        LocalDateTime nowTime = LocalDateTime.now();

        Optional.ofNullable(jwtSetting.getTokenIssuer()).orElseThrow(() -> {
            return new IllegalArgumentException("jwt.tokenIssuer 发布时间不能为空");
        });

        Optional.ofNullable(jwtSetting.getTokenExpirationTime()).orElseThrow(() -> {
            return new IllegalArgumentException("jwt.tokenExpirationTime token 过期时间不能为空");
        });

        Optional.ofNullable(jwtSetting.getTokenSigningKey()).orElseThrow(() -> {
            return new IllegalArgumentException("jwt.tokenSigningKey 签名不能为空");
        });

        return Jwts.builder()
                .setClaims(claims)
                .setIssuer(jwtSetting.getTokenIssuer())
                .setIssuedAt(Date.from(nowTime.atZone(ZoneId.systemDefault()).toInstant()))
                .setExpiration(Date.from(nowTime.plusMinutes(jwtSetting.getTokenExpirationTime()).atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(SignatureAlgorithm.HS512, jwtSetting.getTokenSigningKey());
    }


    public SessionContext parseSession(JwtRawAccessToken accessToken) {
        Jws<Claims> claimsJws = null;
        try {
            claimsJws = accessToken.parseClaims(this.jwtSetting.getTokenSigningKey());
        }catch (Exception e){
            e.printStackTrace();
            throw new BadCredentialsException("Token 非法");
        }

        Claims claims = Optional.ofNullable(claimsJws).orElseThrow(() -> {
            return new NonceExpiredException("登陆信息过期");
        }).getBody();

        SessionContext sessionContext = new SessionContext();

        Optional.ofNullable(claims).ifPresent(s -> {
            Collection<GrantedAuthority> scopes = Lists.newArrayList();
            Optional.ofNullable(s.get(SecurityConst.JWT_CLAIMS_SCOPE_KEY)).ifPresent((i)->{
                for(String scope:i.toString().split(",")){
                    scopes.add(new SimpleGrantedAuthority(scope));
                }
            });
            Optional.ofNullable(s.get(SecurityConst.JWT_CLAIMS_USER_KEY)).ifPresent((i)->{
                Map m = (Map) i;
                sessionContext.setSessionUser(JsonUtil.mapToEntity(m,SessionUser.class));
            });
            sessionContext.setAuthorities(scopes);
        });

        return sessionContext;
    }


}
